#pragma once

#include <map>
#include <vector>
#include <d3d9.h>
#include "FakeUnknown.h"

namespace rt { namespace Fakes {


class FakeDirect3D9 : public FakeUnknown, public IDirect3D9
{
public:
	FakeDirect3D9();
	virtual ~FakeDirect3D9();

	CONTAIN_UNKNOWN(Unknown);

    /*** IDirect3D9 methods ***/
    STDMETHOD(RegisterSoftwareDevice)(void *pInitializeFunction);
	bool RegisterSoftwareDeviceCalled;
	void *RegisterSoftwareDeviceLastInitializeFunction;
	HRESULT RegisterSoftwareDeviceFakeHResult;

    STDMETHOD_(UINT, GetAdapterCount)();
	bool GetAdapterCountCalled;
	UINT GetAdapterCountFakeResult;

    STDMETHOD(GetAdapterIdentifier)(UINT Adapter, DWORD Flags, D3DADAPTER_IDENTIFIER9 *pIdentifier);
	bool GetAdapterIdentifierCalled;
	std::vector<UINT> GetAdapterIdentifierLastAdapter;
	std::vector<DWORD> GetAdapterIdentifierLastFlags;
	std::vector<D3DADAPTER_IDENTIFIER9> GetAdapterIdentifierFakeResult;
	std::vector<HRESULT> GetAdapterIdentifierFakeHResult;

    STDMETHOD_(UINT, GetAdapterModeCount)(UINT Adapter, D3DFORMAT Format);
	bool GetAdapterModeCountCalled;
	std::vector<UINT> GetAdapterModeCountLastAdapter;
	std::vector<D3DFORMAT> GetAdapterModeCountLastFormat;
	std::vector<UINT> GetAdapterModeCountFakeResult;

    STDMETHOD(EnumAdapterModes)(UINT Adapter, D3DFORMAT Format, UINT Mode, D3DDISPLAYMODE *pMode);
	bool EnumAdapterModesCalled;
	std::vector<UINT> EnumAdapterModesLastAdapter;
	std::vector<D3DFORMAT> EnumAdapterModesLastFormat;
	std::vector<UINT> EnumAdapterModesLastMode;
	struct EnumAdapterModesFakeResultKey
	{
		UINT adapter;
		D3DFORMAT format;
		UINT mode;
	};
	std::map<EnumAdapterModesFakeResultKey, D3DDISPLAYMODE> EnumAdapterModesFakeResult;
	std::map<EnumAdapterModesFakeResultKey, HRESULT> EnumAdapterModesFakeHResult;

    STDMETHOD(GetAdapterDisplayMode)(UINT Adapter, D3DDISPLAYMODE *pMode);
	bool GetAdapterDisplayModeCalled;
	std::vector<UINT> GetAdapterDisplayModeLastAdapter;
	std::vector<D3DDISPLAYMODE> GetAdapterDisplayModeFakeResult;
	std::vector<HRESULT> GetAdapterDisplayModeFakeHResult;

    STDMETHOD(CheckDeviceType)(UINT Adapter,D3DDEVTYPE DevType,D3DFORMAT AdapterFormat,D3DFORMAT BackBufferFormat,BOOL bWindowed);
	bool CheckDeviceTypeCalled;
	std::vector<UINT> CheckDeviceTypeLastAdapter;
	std::vector<D3DDEVTYPE> CheckDeviceTypeLastDevType;
	std::vector<D3DFORMAT> CheckDeviceTypeLastAdapterFormat;
	std::vector<D3DFORMAT> CheckDeviceTypeLastBackBufferFormat;
	std::vector<BOOL> CheckDeviceTypeLastWindowed;
	std::vector<HRESULT> CheckDeviceTypeFakeHResult;

    STDMETHOD(CheckDeviceFormat)(UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT AdapterFormat,DWORD Usage,D3DRESOURCETYPE RType,D3DFORMAT CheckFormat);
	bool CheckDeviceFormatCalled;
	std::vector<UINT> CheckDeviceFormatLastAdapter;
	std::vector<D3DDEVTYPE> CheckDeviceFormatLastDeviceType;
	std::vector<D3DFORMAT> CheckDeviceFormatLastAdapterFormat;
	std::vector<DWORD> CheckDeviceFormatLastUsage;
	std::vector<D3DRESOURCETYPE> CheckDeviceFormatLastRType;
	std::vector<D3DFORMAT> CheckDeviceFormatLastCheckFormat;
	std::vector<HRESULT> CheckDeviceFormatFakeHResult;

    STDMETHOD(CheckDeviceMultiSampleType)(UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT SurfaceFormat,BOOL Windowed,D3DMULTISAMPLE_TYPE MultiSampleType,DWORD* pQualityLevels);
	bool CheckDeviceMultiSampleTypeCalled;
	std::vector<UINT> CheckDeviceMultiSampleTypeLastAdapter;
	std::vector<D3DDEVTYPE> CheckDeviceMultiSampleTypeLastDeviceType;
	std::vector<D3DFORMAT> CheckDeviceMultiSampleTypeLastSurfaceFormat;
	std::vector<BOOL> CheckDeviceMultiSampleTypeLastWindowed;
	std::vector<D3DMULTISAMPLE_TYPE> CheckDeviceMultiSampleTypeLastMultiSampleType;
	std::vector<DWORD> CheckDeviceMultiSampleTypeFakeQualityLevels;
	std::vector<HRESULT> CheckDeviceMultiSampleTypeFakeHResult;

    STDMETHOD(CheckDepthStencilMatch)(UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT AdapterFormat,D3DFORMAT RenderTargetFormat,D3DFORMAT DepthStencilFormat);
	bool CheckDepthStencilMatchCalled;
	std::vector<UINT> CheckDepthStencilMatchLastAdapter;
	std::vector<D3DDEVTYPE> CheckDepthStencilMatchLastDeviceType;
	std::vector<D3DFORMAT> CheckDepthStencilMatchLastAdapterFormat;
	std::vector<D3DFORMAT> CheckDepthStencilMatchLastRenderTargetFormat;
	std::vector<D3DFORMAT> CheckDepthStencilMatchLastDepthStencilFormat;
	std::vector<HRESULT> CheckDepthStencilMatchFakeHResult;

    STDMETHOD(CheckDeviceFormatConversion)(UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT SourceFormat,D3DFORMAT TargetFormat);
	bool CheckDeviceFormatConversionCalled;
	std::vector<UINT> CheckDeviceFormatConversionLastAdapter;
	std::vector<D3DDEVTYPE> CheckDeviceFormatConversionLastDeviceType;
	std::vector<D3DFORMAT> CheckDeviceFormatConversionLastSourceFormat;
	std::vector<D3DFORMAT> CheckDeviceFormatConversionLastTargetFormat;
	std::vector<HRESULT> CheckDeviceFormatConversionFakeHResult;

    STDMETHOD(GetDeviceCaps)(UINT Adapter,D3DDEVTYPE DeviceType,D3DCAPS9* pCaps);
	bool GetDeviceCapsCalled;
	std::vector<UINT> GetDeviceCapsLastAdapter;
	std::vector<D3DDEVTYPE> GetDeviceCapsLastDeviceType;
	std::vector<D3DCAPS9> GetDeviceCapsFakeResult;
	std::vector<HRESULT> GetDeviceCapsFakeHResult;

    STDMETHOD_(HMONITOR, GetAdapterMonitor)(UINT Adapter);
	bool GetAdapterMonitorCalled;
	std::vector<UINT> GetAdapterMonitorLastAdapter;
	std::vector<HMONITOR> GetAdapterMonitorFakeResult;

    STDMETHOD(CreateDevice)(UINT Adapter,D3DDEVTYPE DeviceType,HWND hFocusWindow,DWORD BehaviorFlags,D3DPRESENT_PARAMETERS* pPresentationParameters,IDirect3DDevice9** ppReturnedDeviceInterface);
	bool CreateDeviceCalled;
	std::vector<UINT> CreateDeviceLastAdapter;
	std::vector<D3DDEVTYPE> CreateDeviceLastDeviceType;
	std::vector<HWND> CreateDeviceLastFocusWindow;
	std::vector<DWORD> CreateDeviceLastBehaviorFlags;
	std::vector<D3DPRESENT_PARAMETERS> CreateDeviceLastPresentationParameters;
	std::vector<IDirect3DDevice9 *> CreateDeviceFakeDeviceInterface;
	std::vector<HRESULT> CreateDeviceFakeHResult;
};

inline bool operator<(FakeDirect3D9::EnumAdapterModesFakeResultKey const &lhs,
					  FakeDirect3D9::EnumAdapterModesFakeResultKey const &rhs)
{
	if (lhs.adapter == rhs.adapter)
	{
		if (lhs.format == rhs.format)
		{
			return lhs.mode < rhs.mode;
		}
		else
		{
			return lhs.format < rhs.format;
		}
	}
	return lhs.adapter < rhs.adapter;
}

} }
